#include "fileConfig.h"
#include "header.h"
#include<openssl/md5.h>
#include<openssl/sha.h>


// extern string toBinary(char *hex);
extern string convertToHex(char *binary);
// extern void gen_random(char *s) ;

fileConfig::fileConfig(){}

int do_sha1_file(const char *name, unsigned char *out,uint16_t offset)
{
	SHA_CTX sc;
	char c;
	char buf[512]="";
	unsigned char bufFinal[512]="";
	
	ifstream src;
	src.open(name,fstream::binary);
	src.seekg(offset,ios::beg);
	if (!src.is_open())
	{
		logFile->WriteFilen("Error : writing to Permanent Memory\n");
		return false;
	}

	
	if (src.is_open())
	{	
		SHA1_Init(&sc);
			while ( src.good() )
			{
				//c =src.get(); 
				src.getline (buf,1);
				if(src.good())
				{
					memcpy(bufFinal,buf,1);
					SHA1_Update(&sc,bufFinal, 1);
					cout << c;
				}
			}
			SHA1_Final(out, &sc);
		src.close();
	}
    return 0;
}

void fileConfig::printIndexDetails()
{
	printf("In print index details \n");
	//logFile->WriteFilen("Initializing Maps");
	printf("hi");
	kwrd_index_file = new LogFile(node.getHomeDir()+"/kwrd_index",2);
	sha1_index_file = new LogFile(node.getHomeDir()+"/sha1_index",2);
	name_index_file = new LogFile(node.getHomeDir()+"/name_index",2);
		
	std::map<string,std::list<int> >::iterator ii1;
	std::list<int>::iterator ii2;
				
	for(ii1=name_index.begin();ii1!=name_index.end();ii1++)
	{
		for(ii2=ii1->second.begin();ii2!=ii1->second.end();ii2++)
		{
	       name_index_file->WriteFile("'"+ii1->first+"'='");
	       name_index_file->WriteFile((uint32_t)(*ii2));
	       name_index_file->WriteFile("'");
	       name_index_file->WriteFile("\n");
		}
	}
	
	for(ii1=sha1_index.begin();ii1!=sha1_index.end();ii1++)
	{
		for(ii2=ii1->second.begin();ii2!=ii1->second.end();ii2++)
		{
	       sha1_index_file->WriteFile("'"+convertToHex((char *)(ii1->first).c_str())+"'='");
	       sha1_index_file->WriteFile((uint32_t)(*ii2));
	       sha1_index_file->WriteFile("'");
	       sha1_index_file->WriteFile("\n");
		}
	}
	for(ii1=kwrd_index.begin();ii1!=kwrd_index.end();ii1++)
	{
		for(ii2=ii1->second.begin();ii2!=ii1->second.end();ii2++)
		{
	       kwrd_index_file->WriteFile("'"+ii1->first+"'='");
	       kwrd_index_file->WriteFile((uint32_t)(*ii2));
	       kwrd_index_file->WriteFile("'");
	       kwrd_index_file->WriteFile("\n");
		}
	}
	
	printf("At the end of print index function");

}


void fileConfig::UpdateIndexFiles(string filename,string sha1,string bitvector)
{

	printf("In update index file function\n");
	std::list<int> newValueList,appendList;

	int index= fileMap.end()->first;
	newValueList.push_back(index);

	// if this index is not present in the name_index
	if(name_index.find(filename)== name_index.end())
	{
		name_index.insert(std::pair<string,std::list<int> >(filename,newValueList));
	}
	else
	{
	   appendList=name_index.find(filename)->second;
	   appendList.push_back(index);
	   name_index.find(filename)->second=appendList;
	}
	if(sha1_index.find(sha1)==sha1_index.end())
	{
	   sha1_index.insert(std::pair<string,std::list<int> >(sha1,newValueList));
	}
	else
	{
	   appendList=sha1_index.find(sha1)->second;
	   appendList.push_back(index);
	   sha1_index.find(sha1)->second=appendList;
	}
	if(kwrd_index.find(bitvector)==kwrd_index.end())
	{
	   kwrd_index.insert(std::pair<string,std::list<int> >(bitvector,newValueList));
	}
	else
	{
	   appendList=kwrd_index.find(bitvector)->second;
	   appendList.push_back(index);
	   kwrd_index.find(bitvector)->second=appendList;
	}
	printf("At the end of the update index file function\n");
}

void fileConfig::bitvector(list<string> keywords)
{
printf("In bit vector function---------------------------------\n");
	bitset<1024> bitvector;
	bitvector.reset();
	list<string>::iterator it;
    for(it=keywords.begin(); it!=keywords.end(); ++it)
	{
		char sha1_buf[SHA_DIGEST_LENGTH];
	
		SHA1((unsigned char *)(*it).c_str(), strlen((*it).c_str()), (unsigned char *)sha1_buf);
		int y = 1;
		int j = 255;
		y = y & (int) sha1_buf[18];
		j = j & (int) sha1_buf[19];
		int k = ((256*y) + j)+512;
		//printf("K = %d\n", k);
		bitvector.set(k);

		MD5_CTX md5;
		uint8_t md5digest[MD5_DIGEST_LENGTH];
		MD5_Init(&md5);
		MD5_Update(&md5, (*it).c_str(), strlen((*it).c_str()));

		MD5_Final(&md5digest[0], &md5);

		int a = 1;
		int b = 255;
		a = a & (int) md5digest[14];
		b = b & (int) md5digest[15];
		int c = ((256*a) + b);
		//printf("MD5 = %d\n",c);
		bitvector.set(c);
	
		int z=0;
		for(int i=1023;i>=0;i=i-4)
		{
			uint8_t sum=bitvector[i]*8+bitvector[i-1]*4+bitvector[i-2]*2+bitvector[i-3];
			bitvectorBuffer[z]=(char)sum;
			//printf("%x",buffer[z]);
			z++;
		}

	}
	for(int i=0;i<256;i++)
	{
		printf("BVector-->%x",bitvectorBuffer[i]);
	}
}


void fileConfig::CreateMetaDataFile(char **cmdLineData,string indexPosition)
{

printf("in CreateMetaDataFile..............................");
	string filename(cmdLineData[1]);
	string metadataFilename(indexPosition);
	metadataFilename+= ".meta";

	LogFile *metadataLog = new LogFile(metadataFilename,2);
	
	stringstream metadataFinalString;
	list<string> keywords;
	
	//get the file size
	struct stat filestat;
	stat( filename.c_str(), &filestat );
	
	unsigned char sha1_result[20]="";
	unsigned char *nonce=(unsigned char *)malloc(20*sizeof(char));
	unsigned char *password = (unsigned char *)malloc(20*sizeof(char));
	char *finalNonce = (char*)malloc(41*sizeof(char));
	char *finalSha1 = (char*)malloc(41*sizeof(char));
	unsigned char *fileID= (unsigned char *)malloc(20*sizeof(char));
	bitset<1024> bitVector1;
	
	
	// all signed chars
	char sha1_result1[20]="";
	char *nonce1=(char *)malloc(20*sizeof(char));
	char *password1 = (char *)malloc(20*sizeof(char));
	char *fileID1= (char *)malloc(20*sizeof(char));
			
	char obj_type[20];
	memset(obj_type,'\0',20*sizeof(char));
	strncpy(obj_type,"file",20);
	obj_type[strlen(obj_type)+1] = '\0';
		
	GetUOID(node.getNodeInstanceID(),obj_type,fileID,20);
	
	memset(sha1_result,0,20);
	memset(sha1_result1,0,20);
	memset(nonce,0,20);
	memset(nonce1,0,20);
	memset(password,0,20);
	memset(password1,0,20);
	memset(fileID,0,20);
	memset(fileID1,0,20);
	memset(finalNonce,0,20);
	memset(finalSha1,0,20);

	//write meta data content 1. filename 2. filesize
	metadataFinalString<<"[metadata]\nFileName="<<filename<<"\nFileSize="<<filestat.st_size;
	
	//3. calculate sha 1
	
	 //FILE *fp; 
	do_sha1_file((indexPosition+".data").c_str(),sha1_result,0);
	metadataFinalString<<"\nSHA1=";


	for (int i=0; i < 20 ; i++) {
		sprintf(&finalSha1[2*i],"%02x",(unsigned char )sha1_result[i]);
	}
	metadataFinalString<<finalSha1;
	
	//4. calculate nonce
	char obj_type1[20];
	memset(obj_type1,'\0',20*sizeof(char));
	strncpy(obj_type1,"metadata",20);
	obj_type1[strlen(obj_type1)+1] = '\0';
	
	metadataFinalString<<"\nNonce=";
	GetUOID(node.getNodeInstanceID(),obj_type1,password,20);
	SHA1((const unsigned char *)password, 20,nonce);
	for (int i=0; i < 20 ; i++) {
		sprintf(&finalNonce[2*i],"%02x",(unsigned char)nonce[i]);
	}
	metadataFinalString<<finalNonce;

	//5. keywords
	metadataFinalString<<dec<<"\nKeywords=";
	int i=3;
	while(cmdLineData[i]!=NULL)
	{
		string keyword(cmdLineData[i++]);
		std::string::size_type k = 0;
		while((k=keyword.find('"',k))!=keyword.npos) 
		{
			keyword.erase(k, 1);
		}
		metadataFinalString<<keyword<<" ";
		keywords.push_back(keyword);
	}

	// 5. bit vector - pending
	memset(bitvectorBuffer,'\0',256);
	bitvector(keywords);
	metadataFinalString<<"\nBit-vector= "<<bitvectorBuffer;
	
	metadataLog->WriteFile(metadataFinalString.str());

	//create Metadata object , FileDetails object 
	memcpy(password1,password,20);
	std::string password2(password1,20);
	
	memcpy(sha1_result1,sha1_result,20);
	std::string sha1_result2(sha1_result1,20);
	memcpy(nonce1,nonce,20);
	
	memcpy(fileID1,fileID,20);
			
	metadata md_object(filename,(uint32_t)filestat.st_size,(char *)sha1_result1,(char *)nonce1,keywords,bitVector1,fileID1);
	fileDetails fileDtls(md_object,true,password2);
	
	// // insert into map
	fileMap.insert(std::pair<int,fileDetails> (fileMap.end()->first+1,fileDtls));
		
	UpdateIndexFiles(filename,sha1_result2,bitVector1.to_string());
	
	free(nonce);
	free(nonce1);
	free(password);
	free(password1);
	free(finalNonce);
	free(finalSha1);
	free(fileID);
	free(fileID1);
	//delete(metadata);
}




bool fileConfig::WriteToPermanentLocation(string filename,string indexPosition,int offset)
{
	printf("in WriteToPermanentLocation................\n");
	indexPosition += ".data";
	ifstream src;
	ofstream des;
	src.open(filename.c_str(),fstream::binary);
	des.open(indexPosition.c_str(),fstream::trunc|fstream::binary);
	src.seekg(offset,ios::beg);
	if (!src.is_open() || !des.is_open())
	{
		logFile->WriteFilen("Error : writing to Permanent Memory\n");
		return false;
	}

	// write to the file
	des<<src.rdbuf();
	
	if (des.bad()==true)
	{
		logFile->WriteFilen("Error : writing to Permanent Memory\n");
		return false;
	}
	src.close();
	des.close();
	return true;
}


bool fileConfig::StoreAtPermanentLocation(char **cmdLineData)
{
printf("in StoreAtPermanentLocation...................\n");
	// calculate the index from the map
	int i=fileMap.end()->first+1;
	stringstream buffer;
	buffer<<node.getHomeDir()+"/"+"files/"<<i;
		
	// if writing to permanent location is successful
	if(WriteToPermanentLocation(cmdLineData[1],buffer.str(),0)==false)
	{
		return false;
	}	

	//creates the FileInfo Object and add it to the map of files.
	CreateMetaDataFile(cmdLineData,buffer.str());
	return true;
}



